#include <jni.h>
#include <string>
#include "md5.h"
#include <android/log.h>
#include <malloc.h>

#define TAG "JNI_TAG"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)



// 额外的字符串。
//static char *EXTRA_SIGNATURE = "Tomcat";
//using namespace std; // 命名空间 std::string signature_str(params_);
//string EXTRA_SIGNATURE = "DARREN";
// 校验签名
static int is_verify = 0;
//static char *PACKAGE_NAME = "com.tom.myjni"; // 包名
// 通过java拿到签名
//static char *APP_SIGNATURE ="308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3233303932323036353234385a180f32303533303931343036353234385a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a02820101009daac59ed420a65c86f52f0c3f50aeb91a09764205fc442c425f20e16eaae5383d4bdfcbc4f34a46ad359e4d75c449fc5c7c6baf6acb2870dcce290c8aa875fa908b7d6419a0d3e035498890493adfec3c2e397766cf335f1790c319cd921f576e31d91eea102d4c175f265e8079959fdc884973ccd3df2c37b66bf72ee2b331c9d8364cf297d47840d97a431ca35dd6134ef8aea6b300146f4fa8b77920c5668b1346236450cbfcd41ba1870213bc5e59067243b0a4370ccff0ff77da577a5317df9e9e625d617e4d60a1e3dca0d7e2416d8b7b303e4e9da86f322df095fd8f758dd178fded1fd299c05e7062c84ecb9095866d1c6b7fed1f1fabad84e9f4070203010001300d06092a864886f70d010105050003820101000ac7b1d4b44cafb7e01cacf92ff07059aa02a9a5a00761877d366249ec1d08a89f3492a909a7be01d89be1c0d171ce6db0bdad28530a4623dde0a4b1accdb15d3356f53882091a82da8037b659b7c217e1437d43d1e569ad8e53a651bffb593b907d2140576d5066b6f0c584e3aeda697b19ec32c9e9d5636cf33d9e745af6c09e2db30ec7f2ca08116d94dd5e2d9d6132f218b1f54cb257c364a2eb7ea6b27774f9ef7fcb7792ac7d150b524b7c83f69b3a1a5f2f588836f95a85c3d744fd966f54e6423b6347a5b160ca86050dcb6657bd34e0f21e4c03f6b1ae1d3542b2688aef635a265f189fd3a9a6b03d4391c4c2582e8c7d40d4b8058694de4bcfafe9";
//static char *APP_SIGNATURE = "308201dd30820146020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b3009060355040613025553301e170d3137303730383136313933395a170d3437303730313136313933395a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330819f300d06092a864886f70d010101050003818d0030818902818100822589ca5971332c35e37a043d57c3ef765a8c966985480a3cdd2853990c16bb13c707afe8e99ada2b1f209e3667989b9643eb96e131642d137f7d5bb31a788e197c167b93a525a5335e193a2f2960979c75052ef3efc32171adf80ebdc0c3320bcbfd8af4d1b5b31fa07b2748c44375346b0b1198cda44ee629239e0b3bcfc90203010001300d06092a864886f70d0101050500038181007f9fe033b562999768570f4deaba1e48aeead2db44dda9b5e8f36a87697e39984c110e15555a25714b068d0d14f5a687db749f6b9cb209f35767f680cb21f2bc18153de0536decca9292c7c775d9f66ad50d3534daf4ed84e2b84c60978cd4c24357f7dc4361f73369926a12f67e3554dd5a5de0614e290e7912a30f1757806f";

extern "C" JNIEXPORT jstring JNICALL
Java_com_tom_myjni_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++ 1225";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT void JNICALL
Java_com_tom_myjni_SignatureUtils_signatureVerify(JNIEnv *env, jclass clazz, jobject context) {
    // TODO: implement signatureVerify()
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_tom_myjni_SignatureUtils_signatureParams(JNIEnv *env, jclass clazz, jstring params_) {
    if (is_verify == 0) { // 未校验前面
        return env->NewStringUTF("error_signature");
    }

//    const char *params = env->GetStringUTFChars(params_, 0);
//    // 1.MD5加密规则：前面添加内容，后面去掉内容。
//    string signature_str(params);
//    signature_str.insert(0, EXTRA_SIGNATURE);
//    // 2.去掉后面两位
//    signature_str = signature_str.substr(0, signature_str.length() - 2);
//
//    // 3.md5加密:C++需要用户自己回收内存
//    MD5_CTX  *ctx = new MD5_CTX();
//    MD5Init(ctx);
//    MD5Update(ctx, (unsigned char *) signature_str.c_str(), signature_str.length());
//    unsigned char digest[16] = {0};
//    MD5Final(digest, ctx);
//    // 生成32位字符串: 需要处理'\0' ???
//    char md5_str[33] = {};
//    // __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","签名校验成功: %s", md5_str);
//    for (int i = 0; i < 16; i++) {
//        // 不足的情况下补0: f = 0f, ab = ab
//        sprintf(md5_str, "%s%02x", md5_str, digest[i]);
//    }
//    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","签名成功: %s", md5_str);
//
//    // 4.释放
//    env->ReleaseStringUTFChars(params_, params);
////    return env->NewStringUTF(signature_str.c_str());
//    return env->NewStringUTF(md5_str); // f4c211b6257770ae0ce075d967b2365a
}

/*
 *
*/
/*
    PackageInfo packageInfo = null;
    try {
        packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    Signature[] signatures = packageInfo.signatures;
    Log.e("TAG", signatures[0].toCharsString());
 *//*

extern "C"
JNIEXPORT void JNICALL
Java_com_tom_myjni_SignatureUtils_signatureVerify(JNIEnv *env, jclass clazz, jobject context) {
    // 开始校验：比对包名 比较是否一样，获取签名比较 比较是否一样。
    // C调用java代码， 属于JNI的基础。
    // 1. 获取包名
    jclass j_clz = env->GetObjectClass(context);
    jmethodID j_mid = env->GetMethodID(j_clz, "getPackageName", "()Ljava/lang/String;");
    jstring j_package_name = (jstring) env->CallObjectMethod(context, j_mid);
    // 2. 比对包名是否一样
    const char *c_package_name = env->GetStringUTFChars(j_package_name, NULL);
    if (strcmp(c_package_name, PACKAGE_NAME) != 0) { // 包名不一样
        return;
    }
    // __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","包名一致: %s", c_package_name);

    // 3. 获取签名
    // 3.1 获取 PackageManager
    j_mid = env->GetMethodID(j_clz,"getPackageManager","()Landroid/content/pm/PackageManager;");
    jobject pack_manager = env->CallObjectMethod(context,j_mid);
    // 3.2 获取 PackageInfo
    j_clz = env->GetObjectClass(pack_manager);
    j_mid = env->GetMethodID(j_clz,"getPackageInfo","(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jobject package_info = env->CallObjectMethod(pack_manager, j_mid, j_package_name, 0x00000040);
    // 3.3 获取 signatures 数组
    j_clz = env->GetObjectClass(package_info);
    jfieldID j_fid = env->GetFieldID(j_clz,"signatures","[Landroid/content/pm/Signature;");
    jobjectArray signatures = (jobjectArray) env->GetObjectField(package_info, j_fid);
    // 3.4 获取 signatures[0]
    jobject signatures_first = env->GetObjectArrayElement(signatures,0);
    // 3.5 调用 signatures[0].toCharsString();
    j_clz = env->GetObjectClass(signatures_first);
    j_mid = env->GetMethodID(j_clz,"toCharsString","()Ljava/lang/String;");
    jstring j_signature_str = (jstring) env->CallObjectMethod(signatures_first, j_mid);
    const char * c_signature_str = env->GetStringUTFChars(j_signature_str,NULL);

    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","A_S1: %s", APP_SIGNATURE);
    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","A_S2: %s", c_signature_str);
    // 4. 比对签名是否一样: 完全一样， 返回0；只要有一个不一样，返回非0（-1）.
    if (strcmp(c_signature_str, APP_SIGNATURE) != 0) {
        return;
    }
    __android_log_print(ANDROID_LOG_ERROR,"JNI_TAG","签名校验成功: %s",c_signature_str);
    // 签名认证已经成功
    is_verify = 1;
}

*/




// C方法集中声明。
extern "C"
{
JNIEXPORT void JNICALL
Java_com_tom_myjni_fmpeg_VideoCompress_compressVideo(JNIEnv *env, jobject instance,
                                                     jobjectArray compressCommand,
                                                     jobject callback);
// 声明方法(实现的方式就是基于命令，windows )
// argc 命令的个数
// char **argv 二维数组
int ffmpegmain(int argc, char **argv, void(call_back)(int, int));
}

// 回调函数 call_back，通过函数指针调用
static jobject call_back_jobj;
static JNIEnv *mEnv; // env不释放。
void call_back(int current, int total){ // 传递函数指针进去。
    // LOGE("压缩进度：%d/%d", current, total);
    // 把进度回调出去 对象是 jobject callback
    if(call_back_jobj != NULL && mEnv != NULL){
        // C进阶再去讲 指针，什么是内存，内存模型 , 获取 j_mid 是会被多次执行
        //TODO 全局缓存j_clazz，j_mid，避免每次更新进度都调用这两个方法。
        jclass j_clazz = mEnv->GetObjectClass(call_back_jobj);
        // javap 命令也能打印 方法的签名。C调用Java的方法。
        jmethodID j_mid = mEnv->GetMethodID(j_clazz,"onCompress","(II)V");
        mEnv->CallVoidMethod(call_back_jobj, j_mid, current, total); // CallObjectMethod
    }
}


extern "C" JNIEXPORT void JNICALL
Java_com_tom_myjni_fmpeg_VideoCompress_compressVideo(JNIEnv *env, jobject instance,
                                                     jobjectArray compressCommand,
                                                     jobject callback) {
    // ffmpeg 处理视频： ffmpeg -i test.mp4 -b:v 1024k out.mp4
    call_back_jobj = env->NewGlobalRef(callback); // 全局方法赋值。
    mEnv = env;
    // ffmpeg 处理视频的压缩，自己处理怎么处理，for循环每一帧 ，对每一帧进行处理（算法）
    // armeabi 这个里面的so都是用来处理音视频的, include 都是头文件
    // main/cpp 还有几个没有被打包编译成 so ,因为这些不算是音视频的处理代码，只是我们现在支持命令（封装）

    // 1. 获取命令的个数
    int argc = env->GetArrayLength(compressCommand);
    // 2. 给 char **argv 填充数据
    char **argv = (char **) malloc(sizeof(char*) * argc);
    for (int i = 0; i < argc; ++i) { // 循环填充数据
        jstring j_param = (jstring) env->GetObjectArrayElement(compressCommand, i);
        argv[i] = (char *) env->GetStringUTFChars(j_param, NULL); // jstring 转 char*
        LOGE("参数：%s", argv[i]);
    }
    // 3. 调用命令函数去压缩，回调处理。
    ffmpegmain(argc, argv, call_back); // 执行头文件里面的方法，链接到so

    // 4. 释放内存
    for (int i = 0; i < argc; ++i) {
        free(argv[i]);
    }
    free(argv);
    env->DeleteGlobalRef(call_back_jobj); // 释放全局回调方法
}

